#include <nanobdd.h>
/**
 * Dev notes:
 * 1. We must have node cache "level, left, right". When cache not hit, a new
 * bdd node will be made, there will be multiple bdd nodes for the same
 * expression, and the == operator will not work in this case.
 */
namespace nanobdd {

NodeTable Nanobdd::nodeTable_ = NodeTable(10000, 1000);
int Nanobdd::cacheSize = 1000;
Nanobdd::Nanobdd(size_t nodeSize, size_t icacheSize)
    : nodeSize_(nodeSize), cacheSize_(icacheSize), nodes_(nodeSize) {
  // cache_.resize(cacheSize);
  // nodecache_.resize(cacheSize);

  Nanobdd::falseNode = new Node(0, nullptr, nullptr);
  // Nanobdd::falseNode->value(false);
  // Nanobdd::falseNode->hash(0);

  Nanobdd::trueNode = new Node(0, nullptr, nullptr);
  // Nanobdd::trueNode->value(true);
  // Nanobdd::trueNode->hash(1);
}

void
Nanobdd::createVars(int num) {
  for (int i = 0; i < num; ++i) {
    createVar(i);
  }
}

Bdd&
Nanobdd::createVar(int id) {
  auto node = makeNode(id, falseNode, trueNode);
  vars_.emplace(vars_.begin() + id, node);
  return vars_.at(id);
}

Bdd&
Nanobdd::getVar(int id) {
  return vars_.at(id);
}

Node*
Nanobdd::makeNode(int level, Node* low, Node* high) {
  return &(nodeTable_(level, low, high));
  // auto cached = cache_.lookup(level, low, high);
  // if (cached.has_value()) {
  //   return cached.value();
  // }

  auto hash = TRIPLEp(level, low, high) % Nanobdd::cacheSize;
  auto entry = nodecache_.lookup(hash, level, low, high);
  // if (entry.has_value()) {
  if (entry != nullptr) {
    return entry;
    // return entry.value();
  }

  // auto iter = nodecache_.find({level, low.get(), high.get()});
  // if (iter != nodecache_.end()) {
  //   return iter->second;
  // }

  auto node = new Node(level, low, high);
  nodecache_.insert(hash, node);
  // nodecache_[std::make_tuple(level, low.get(), high.get())] = node;
  return node;
}

} // namespace nanobdd